The TADS Alternate Library
Version 2.0

Messages and Classes


Copyright 2000 by Kevin Forchione.
This is part of the TADS Alternate Library Authors Manual.

Introduction and Table of Contents




TADS 2 Object-oriented approach

 

TADS is a very object-oriented programming language. Although lacking both encapsulation and polymorphism it does employ a sophisticated system of multiple inheritance.        Games are constructed of objects that are constantly communicating with one another and the parser via messages.

 

Messages

 

Objects communicate with one another by sending messages. Each message can result in a reply, in the form of a return value. The basic formats for messages are:

 

            objectname.messagename

objectname.message(arg1, arg2, …);

 

For example, when a player types <<look>> the player object’s location is sent the following command:

 

            actor.location.lookAround(true);

 

The lookAround() method, in turn, sends a message to the actor.location.nrmLkAround(), which in turn sends a message to actor.location.lDesc. In VENTURE the forestClearing lDesc is as follows:

 

    lDesc = "This small clearing is covered in a thick bed of grasses

        and heavy frond leaves of the trees that form a near-

        impenetrable barrier encircling it. The air is heavy and

        sweltering, seemingly drawing out swarms of tiny blue-bottle

        flies that dart about drawn to the rotting vegetation."

 

The lDesc property defines a set of rules for handling the message being sent. In this example nothing is returned from lDesc. Instead the string is evaluated and sent to the text formatter, which displays the room’s long description. 

 

Classes

 

Classes are templates for objects. Each object is an instance of a class. Even the library is built largely of classes. The Alt library expands upon the basic class library of ADV.T. A class consists of attributes and methods. These distinctions can be a little blurred in TADS, because of its lack of data-typing. For instance:

 

    lDesc = {"This small clearing is covered in a thick bed of grasses

        and heavy frond leaves of the trees that form a near-

        impenetrable barrier encircling it. The air is heavy and

        sweltering, seemingly drawing out swarms of tiny blue-bottle

        flies that dart about drawn to the rotting vegetation.";}

 

is clearly a method, but in the previous example lDesc is defined as an attribute.

 

Object and Class Definitions

 

We’ve seen a couple of examples of object definitions. In TADS there is very little distinction between an object definition and a class definition. Class definitions are ignored by the parser and such built-in functions as firstobj() and nextobj(). The class keyword is required only when an author wishes that a definition be treated like a class and not an object by the parser and the definition contains a location property or vocabulary.

 

The basic format for a class is:

 

class classname: classlist

       properties

;

 

The class keyword differentiates the definition from an object definition. The classlist can consist of any number of classnames separated by commas. A class that does not inherit from any other classes must inherit from object. Properties consist of attributes and methods, and are optional – a class can be defined with no properties at all, as demonstrated by the Alt Room class definition.

 

class Room: Roomable, Surface

;

 

In this example Room doesn’t define any properties of its own, but inherits those of Roomable and Surface classes.

 

Properties

 

Properties are further divided into attributes and methods. Because TADS doesn’t enforce datatyping, however, the distinction between an attribute and a method can become a little blurred. For instance, the following is an attribute:

 

            lDesc = ‘hello’

 

The value of lDesc has been initialised as a single-quoted string. While the next example is a method:

 

            lDesc = { “This is a method.”; }

 

But what about this?

 

            lDesc = “%You% see%s% nothing unusual about <<self.theDesc>>.”

 

Assigning a double-quoted string appears to put the property into the attribute category, but double-quoted strings aren’t values in TADS, instead the string is evaluated, and the output is sent to the text formatter. This means that double-quoted strings are shorthand for code blocks that in essence say:

 

            lDesc = {

                        say(‘%You% sees%s% nothing unusual about ‘);

                        self.theDesc;

                        say( ‘.’ );

            }

 

Alt adopts Java-style naming rules for classes, attributes, methods and functions. See Coding Conventions.

 

Messages

 

The parser is sending messages to objects all the time. But besides messages sent from the parser, objects can send messages to each other. This can be very useful when you wish to associate one event with another. For example, somewhere in our forest clearing there is an ominous-looking raven perched on the limb of a gnarled old tree.

 

gnarledTree: FixedItem

    location = forestClearing

    noun = 'tree'

    adjective = 'gnarled' 'old' 'very'

    sDesc = "gnarled old tree"

    lDesc = {

        "It looks as though it's been there for hundreds of years. ";

        if (raven.isIn(self))

            "\^<<raven.aDesc>> <<raven.isDesc>> <<raven.posture.desc>>

            high up in one of its branches.";

    }

    initial = "At the edge of the clearing stands a very old gnarled

        tree. "

;

 

The tree isn’t really a Surface or a Container, it’s merely a FixedItem, which means it can’t be taken, moved, thrown, or put anywhere. Additionally it doesn’t appear in room descriptions, but the initial attribute is used to display a suitable description. When the player examines the tree closer using <<look at>> or <<examine>> they will find that a raven is perching in one of its branches.

 

 

    lDesc = {

        "It looks as though it's been there for hundreds of years. ";

        if (raven.isIn(self))

            "\^<<raven.aDesc>> <<raven.isDesc>> <<raven.posture.desc>>

            high up in one of its branches.";

    }

 

Our raven is described using the Alt description attributes, aDesc, isDesc, and posture.desc. These allow an author to generate generalised descriptions for classes of objects.

 

Now for our raven:

 

raven: Animate

    location = gnarledTree

    noun = 'raven'

    adjective = 'ominous' 'ominous-looking'

    sDesc = "raven"

    lDesc = "It's an ominous-looking raven! It seems to be watching

        you!"

    actorDesc = "An ominous looking raven is here, watching you

        intently."

    squawk(utterance) = {

        if (self.isIn(gLocation(sound)))

            "\bSquawk <<self.theDesc>>, \"<<utterance>> <<utterance>>\"";

 

    }

    posture = perching

;

 

The raven is an Animate object, which means that it simulates behaviours of life. In addition to an lDesc it possesses an actorDesc, which is displayed whenever the raven is directly contained within the room. Because it’s location is the gnarledTree we won’t see this description when we first encounter the bird, but it’s a good idea to code an actorDesc anyway, to add atmosphere to the object.

 

Our raven overrides the Animate posture attribute, which is standing by default. Since Alt doesn’t define perching as a posture (see Object Postures) so we must define it in our game code.

 

perching: Posture

    desc = "perching"

;

 

Notice too that our raven defines a squawk() method, which is a new method defined to allow the bird to make utterances from time to time.

 

We can then add something like the following to our redBerry dobjPostAction rules:

 

            case dropVerb:

                "The berry drops to the ground, battered slightly. ";

                raven.squawk('Nevermore!');

                return true;

 

Now when we drop the berry the raven utters its prophetic statement.

 

>drop berry

The berry drops to the ground, battered slightly.

 

Squawk the raven, "Nevermore! Nevermore!"

 

This same method can be sent other single-quoted strings as arguments for generating responses to other actions. Since only the raven defines a squawk() method it would be an error to send a squawk message to any other game object. However, Alt’s class inheritance mechanism allows an author to define a squawk() method at a class level, which can then be inherited by instances of the class.

 

Inheritance

 

One extremely useful property of classes is inheritance. TADS allows for multiple inheritance, and the Alt library takes advantage of this feature. TADS inheritance mechanism offers great flexibility and control in your object definitions.

 

To illustrate, suppose we define a Bird class, from which raven is an instance.

 

class Bird: Animate

            posture = perching

            squawk(utterance) = {

              if (self.isIn(gLocation(sound)))

                                    “\^<<self.theDesc>> squawks, \”<<utterance>> <<utterance>>\””;

            }

;

 

Our raven could then be defined as:

 

raven: Bird

    location = gnarledTree

    noun = 'raven'

    adjective = 'ominous' 'ominous-looking'

    sDesc = "raven"

    lDesc = "It's an ominous-looking raven! It seems to be watching

        you!"

    actorDesc = "An ominous looking raven is here, watching you

        intently."

    squawk(utterance) = {

        if (self.isIn(gLocation(sound)))

            "\bSquawk <<self.theDesc>>, \"<<utterance>> <<utterance>>\"";

 

    }

;

 

There is no need to override the posture attribute for raven, since it inherits a posture of perching from Bird. We would, however, like to override the squawk() method to tailor it to our raven.

 

TADS provides two very useful means of passing control to superclasses. The first is the pass statement, the second is the inherited statement. Alt library takes full advantage of these two statements in its class definitions.

 

class Treasure

            points = 10

            dobjPostAction = {